Completed
Pull Request — develop (#75)
by
unknown
01:06
created

forcegraph.js ➔ ... ➔ d3Drag.drag.end   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
nc 2
dl 0
loc 7
rs 9.4285
nop 0
1
define(['d3-selection', 'd3-force', 'd3-zoom', 'd3-drag', 'forcegraph/math', 'forcegraph/draw'], function (d3Selection, d3Force, d3Zoom, d3Drag, math, draw) {
2
  'use strict';
3
4
  return function (config, linkScale, sidebar, router) {
5
    var self = this;
6
    var el;
7
    var canvas;
8
    var ctx;
9
    var force;
10
    var forceLink;
11
12
    var transform = d3Zoom.zoomIdentity;
13
    var intNodes = [];
14
    var dictNodes = {};
15
    var intLinks = [];
16
17
    const NODE_RADIUS_DRAG = 10;
18
    const NODE_RADIUS_SELECT = 15;
19
    const LINK_RADIUS_SELECT = 12;
20
21
    const ZOOM_MIN = 1 / 4;
22
    const ZOOM_MAX = 3;
23
24
    draw.setTransform(transform);
25
26
    function resizeCanvas() {
27
      canvas.width = el.offsetWidth;
28
      canvas.height = el.offsetHeight;
29
      canvas.style.width = el.offsetWidth + 'px';
30
      canvas.style.height = el.offsetHeight + 'px';
31
    }
32
33
    function panTo(x, y) {
34
      transform.x = (canvas.width + sidebar()) / 2  - x / 2 * transform.k;
35
      transform.y =  canvas.height / 2              - y / 2 * transform.k;
36
    }
37
38
    function onClick() {
39
      if (d3Selection.event.defaultPrevented) {
40
        return;
41
      }
42
43
      var e = transform.invert([d3Selection.event.clientX, d3Selection.event.clientY]);
44
      var n = force.find(e[0], e[1], NODE_RADIUS_SELECT);
45
46
      if (n !== undefined) {
47
        router.node(n.o.node)();
48
        return;
49
      }
50
51
      e = {x: e[0], y: e[1]};
52
53
54
      var closedLink;
55
      var radius = LINK_RADIUS_SELECT;
56
      intLinks
57
        /* Disable Clickable VPN
58
        .filter(function (d) {
59
          return d.o.type !== 'fastd' && d.o.type !== 'L2TP';
60
        })
61
        */
62
        .forEach(function (d) {
63
          var distance = math.distanceLink(e, d.source, d.target);
64
          if (distance < radius) {
65
            closedLink = d;
66
            radius = distance;
67
          }
68
        });
69
70
      if (closedLink !== undefined) {
71
        router.link(closedLink.o)();
72
      }
73
    }
74
75
    function redraw() {
76
      ctx.save();
77
      ctx.clearRect(0, 0, canvas.width, canvas.height);
78
      ctx.translate(transform.x, transform.y);
79
      ctx.scale(transform.k, transform.k);
80
81
      intLinks.forEach(draw.drawLink);
82
      intNodes.forEach(draw.drawNode);
83
84
      ctx.restore();
85
    }
86
87
    el = document.createElement('div');
88
    el.classList.add('graph');
89
90
    forceLink = d3Force.forceLink()
91
     .distance(function (d) {
92
       if (d.o.type === 'fastd' || d.o.type === 'L2TP') {
93
         return 0;
94
       }
95
       return 75;
96
     })
97
     .strength(function (d) {
98
       if (d.o.type === 'fastd' || d.o.type === 'L2TP') {
99
         return 0.02;
100
       }
101
       return Math.max(0.5, 1 / d.o.tq);
102
     });
103
104
    var zoom = d3Zoom.zoom()
105
         .scaleExtent([ZOOM_MIN, ZOOM_MAX])
106
         .on('zoom', function () {
107
           transform = d3Selection.event.transform;
108
           draw.setTransform(transform);
109
           redraw();
110
         });
111
112
113
    force = d3Force.forceSimulation()
114
      .force('link', forceLink)
115
      .force('charge', d3Force.forceManyBody())
116
      .on('tick', redraw);
117
118
    var drag = d3Drag.drag()
119
      .subject(function () {
120
        var e = transform.invert([d3Selection.event.x, d3Selection.event.y]);
121
        var n = force.find(e[0], e[1], NODE_RADIUS_DRAG);
122
123
        if (n !== undefined) {
124
          return n;
125
        }
126
        return undefined;
127
      })
128
      .on('start', function () {
129
        if (!d3Selection.event.active) {
130
          force.alphaTarget(0.1).restart();
131
        }
132
        d3Selection.event.subject.fx = transform.applyX(d3Selection.event.subject.x);
133
        d3Selection.event.subject.fy = transform.applyY(d3Selection.event.subject.y);
134
      })
135
      .on('drag', function () {
136
        var e = transform.invert([d3Selection.event.x, d3Selection.event.y]);
137
        d3Selection.event.subject.fx = e[0];
138
        d3Selection.event.subject.fy = e[1];
139
      })
140
      .on('end', function () {
141
        if (!d3Selection.event.active) {
142
          d3Selection.event.subject.fx = null;
143
          d3Selection.event.subject.fy = null;
144
          force.alphaTarget(0);
145
        }
146
      });
147
148
    canvas = d3Selection.select(el)
149
      .append('canvas')
150
      .on('click', onClick)
151
      .call(drag)
152
      .call(zoom)
153
      .node();
154
155
    ctx = canvas.getContext('2d');
156
    draw.setCTX(ctx);
157
158
    window.addEventListener('resize', function () {
159
      resizeCanvas();
160
      redraw();
161
    });
162
163
    self.setData = function setData(data) {
164
      intNodes = data.graph.nodes.map(function (d) {
165
        var e;
166
        if (d.id in dictNodes) {
167
          e = dictNodes[d.id];
168
        } else {
169
          e = {};
170
          dictNodes[d.id] = e;
171
        }
172
173
        e.o = d;
174
175
        return e;
176
      });
177
178
      intLinks = data.graph.links.map(function (d) {
179
        var e = {};
180
        e.o = d;
181
        e.source = dictNodes[d.source.id];
182
        e.target = dictNodes[d.target.id];
183
        e.color = linkScale(d.tq).hex();
184
185
        return e;
186
      });
187
188
      force.nodes(intNodes);
189
      forceLink.links(intLinks);
190
191
      force.restart();
192
      resizeCanvas();
193
    };
194
195
    self.resetView = function resetView() {
196
      draw.setHighlight(null);
197
      transform.k = ZOOM_MIN;
198
      panTo((canvas.width - sidebar()) / 2, canvas.height / 2);
199
      redraw();
200
    };
201
202
    self.gotoNode = function gotoNode(d) {
203
      draw.setHighlight({ type: 'node', o: d });
204
205
      for (var i = 0; i < intNodes.length; i++) {
206
        var n = intNodes[i];
207
        if ( n.o.node === d) {
208
          continue;
209
        }
210
        transform.k = 1;
211
        panTo(n.x, n.y);
212
        break;
213
      }
214
      redraw();
215
    };
216
217
    self.gotoLink = function gotoLink(d) {
218
      draw.setHighlight({ type: 'link', o: d });
219
      redraw();
220
    };
221
222
    self.destroy = function destroy() {
223
      force.stop();
224
      canvas.remove();
225
      force = null;
226
227
      if (el.parentNode) {
228
        el.parentNode.removeChild(el);
229
      }
230
    };
231
232
    self.render = function render(d) {
233
      d.appendChild(el);
234
      resizeCanvas();
235
    };
236
237
    return self;
238
  };
239
});
240